package com.ruoyi.quartz.util

import com.ruoyi.common.constant.Constants
import com.ruoyi.common.constant.ScheduleConstants
import com.ruoyi.common.exception.job.TaskException
import com.ruoyi.common.utils.MyStringUtils
import com.ruoyi.common.utils.spring.SpringUtils
import com.ruoyi.quartz.domain.SysJob
import org.apache.commons.lang3.StringUtils
import org.quartz.*

/**
 * 定时任务工具类
 *
 * @author ruoyi
 */
object ScheduleUtils {
    /**
     * 得到quartz任务类
     *
     * @param sysJob 执行计划
     * @return 具体执行任务类
     */
    private fun getQuartzJobClass(sysJob: SysJob): Class<out Job?> {
        val isConcurrent = "0" == sysJob.concurrent
        return if (isConcurrent) QuartzJobExecution::class.java else QuartzDisallowConcurrentExecution::class.java
    }

    /**
     * 构建任务触发对象
     */
    fun getTriggerKey(jobId: Long?, jobGroup: String?): TriggerKey {
        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup)
    }

    /**
     * 构建任务键对象
     */
    fun getJobKey(jobId: Long?, jobGroup: String?): JobKey {
        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup)
    }

    /**
     * 创建定时任务
     */
    @Throws(SchedulerException::class, TaskException::class)
    fun createScheduleJob(scheduler: Scheduler?, job: SysJob) {
        val jobClass = getQuartzJobClass(job)
        // 构建job信息
        val jobId = job.jobId
        val jobGroup = job.jobGroup
        val jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build()

        // 表达式调度构建器
        var cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.cronExpression)
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder)

        // 按新的cronExpression表达式构建一个新的trigger
        val trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
                .withSchedule(cronScheduleBuilder).build()

        // 放入参数，运行时的方法可以获取
        jobDetail.jobDataMap[ScheduleConstants.TASK_PROPERTIES] = job

        // 判断是否存在
        if (scheduler != null) {
            if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
                // 防止创建时存在数据问题 先移除，然后在执行创建操作
                scheduler.deleteJob(getJobKey(jobId, jobGroup))
            }
            // 判断任务是否过期
            if (MyStringUtils.isNotNull(CronUtils.getNextExecution(job.cronExpression))) {
                // 执行调度任务
                scheduler.scheduleJob(jobDetail, trigger)
            }

            // 暂停任务
            if (job.status == ScheduleConstants.Status.PAUSE.value) {
                scheduler.pauseJob(getJobKey(jobId, jobGroup))
            }
        }


    }

    /**
     * 设置定时任务策略
     */
    @Throws(TaskException::class)
    fun handleCronScheduleMisfirePolicy(job: SysJob, cb: CronScheduleBuilder): CronScheduleBuilder {
        return when (job.misfirePolicy) {
            ScheduleConstants.MISFIRE_DEFAULT -> cb
            ScheduleConstants.MISFIRE_IGNORE_MISFIRES -> cb.withMisfireHandlingInstructionIgnoreMisfires()
            ScheduleConstants.MISFIRE_FIRE_AND_PROCEED -> cb.withMisfireHandlingInstructionFireAndProceed()
            ScheduleConstants.MISFIRE_DO_NOTHING -> cb.withMisfireHandlingInstructionDoNothing()
            else -> throw TaskException("The task misfire policy '" + job.misfirePolicy
                    + "' cannot be used in cron schedule tasks", TaskException.Code.CONFIG_ERROR)
        }
    }

    /**
     * 检查包名是否为白名单配置
     *
     * @param invokeTarget 目标字符串
     * @return 结果
     */
    fun whiteList(invokeTarget: String?): Boolean {
        val packageName: String = StringUtils.substringBefore(invokeTarget, "(")
        val count: Int = StringUtils.countMatches(packageName, ".")
        if (count > 1) {
            return MyStringUtils.myContainsAnyIgnoreCase(invokeTarget, *Constants.JOB_WHITELIST_STR)
        }
        val obj: Any = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0])
        val beanPackageName = obj.javaClass.getPackage().name
        return (MyStringUtils.myContainsAnyIgnoreCase(beanPackageName, *Constants.JOB_WHITELIST_STR)
                && !MyStringUtils.myContainsAnyIgnoreCase(beanPackageName, *Constants.JOB_ERROR_STR))
    }
}
